package com.test.generics;

/**
 *彻底搞懂Java泛型中的PECS原则
 * 在Java中，泛型（Generics）是一个强大的工具，它允许我们在编写代码时定义类、接口和方法时使用类型参数。泛型使得代码更具通用性、类型安全性和可读性。在泛型中，PECS原则是一个非常重要的概念。本文将详细讲解PECS原则，帮助大家更好地理解和应用。
 *
 * 什么是PECS原则？
 * PECS 是 “Producer Extends, Consumer Super” 的缩写。这句话的意思是：生产者（Producer）用 extends，消费者（Consumer）用 super。这个原则在使用泛型限定通配符时非常有用，可以帮助我们决定何时使用 extends 和 super。
 *
 * 两种限定通配符
 * 类型的上界：<? extends T> 表示类型必须为 T 类型或者 T 的子类。
 * 类型的下界：<? super T> 表示类型必须为 T 类型或者 T 的父类。
 * 上代码
 * 为了更好地解释这些概念，我们先定义一些类：
 *
 * public class Fruit {
 * }
 * public class Apple extends Fruit {
 * }
 * public class Banana extends Fruit {
 * }
 * List<? extends Fruit> 的理解
 * 正如字面意思，<? extends Fruit> 表示泛型的类型是 Fruit 或者 Fruit 的子类。也就是说，我们给 list 赋值时，泛型可以是 Fruit 或者 Fruit 的子类，比如 new ArrayList<Fruit>()、new ArrayList<Apple>() 或者 new ArrayList<Banana>()。
 * private static List<? extends Fruit> getExtendsList() {
 *     List<? extends Fruit> list;
 *     list = new ArrayList<Fruit>();
 *     list = new ArrayList<Apple>();
 *     list = new ArrayList<Banana>();
 *     return list;
 * }
 * 具体来说，List<? extends Fruit> 允许我们将 List<Fruit>、List<Apple> 和 List<Banana> 都赋值给它。然而，虽然我们可以从这个列表中获取元素（因为我们知道它们至少是 Fruit 类型），但我们不能向其中添加元素（除了 null），因为编译器无法确定我们实际的列表类型。
 * private static void m1(List<? extends Fruit> list) {
 *     Fruit fruit = list.get(0); // 安全的，因为我们知道列表中至少是Fruit类型
 *     // list.add(new Apple()); // 错误，编译器不允许，因为list可能是new ArrayList<Banana>()
 * }
 * List<? super Fruit> 的理解
 * <? super Fruit> 表示泛型的类型是 Fruit 或者 Fruit 的父类。也就是说，我们给 list 赋值时，泛型可以是 Fruit 或者 Fruit 的父类，比如 new ArrayList<Fruit>() 或者 new ArrayList<Object>()。
 *
 * private static List<? super Fruit> getSuperList() {
 *     List<? super Fruit> list;
 *     list = new ArrayList<Fruit>();
 *     list = new ArrayList<Object>();
 *     return list;
 * }
 * 具体来说，List<? super Fruit> 允许我们向列表中添加 Fruit 或者 Fruit 的子类，但我们不能安全地获取特定类型的元素，只能获取 Object 类型的元素。
 *
 * private static void m2(List<? super Fruit> list) {
 *     list.add(new Apple()); // 可以，因为Apple是Fruit的子类
 *     list.add(new Banana()); // 可以，因为Banana是Fruit的子类
 *     Object object = list.get(0); // 只能是Object类型，因为我们不知道具体的类型
 * }
 * 总结
 * List<? extends Fruit> list：表示泛型的类型是 Fruit 或 Fruit 的子类，一般用于只获取元素。
 * List<? super Fruit> list：表示泛型的类型是 Fruit 或 Fruit 的父类，一般用于只添加元素（获取出来的元素是 Object 类型，泛型意义不大）。
 * List list：明确的泛型，可以获取元素，也可以添加元素，是最常用的泛型。
 * JDK中的PECS
 * 在JDK中，PECS原则被广泛应用。例如，java.util.Collections 类中的 copy 方法：
 *
 * public static <T> void copy(List<? super T> dest, List<? extends T> src) {
 *     int srcSize = src.size();
 *     if (srcSize > dest.size())
 *         throw new IndexOutOfBoundsException("Source does not fit in dest");
 *
 *     if (srcSize < COPY_THRESHOLD ||
 *         (src instanceof RandomAccess && dest instanceof RandomAccess)) {
 *         for (int i = 0; i < srcSize; i++)
 *             dest.set(i, src.get(i));
 *     } else {
 *         ListIterator<? super T> di = dest.listIterator();
 *         ListIterator<? extends T> si = src.listIterator();
 *         for (int i = 0; i < srcSize; i++) {
 *             di.next();
 *             di.set(si.next());
 *         }
 *     }
 * }
 * 在这个方法中，dest 是一个消费者，只接受元素，所以使用 super；而 src 是一个生产者，只提供元素，所以使用 extends。
 *
 * 深入理解泛型通配符
 * 为了更深入理解，我们可以考虑以下几个方面：
 *
 * 协变（Covariance）和逆变（Contravariance）：
 *
 * <? extends T> 是协变的，这意味着如果 Apple 是 Fruit 的子类，那么 List<Apple> 也是 List<? extends Fruit> 的子类型。
 * <? super T> 是逆变的，这意味着如果 Fruit 是 Apple 的父类，那么 List<Fruit> 也是 List<? super Apple> 的父类型。
 * 泛型方法中的PECS：
 *
 * 在定义泛型方法时，PECS原则同样适用。通过明确指定泛型参数的上下界，可以更好地控制方法的输入和输出。
 * public static <T> void addToList(List<? super T> list, T item) {
 *     list.add(item);
 * }
 *
 * public static <T> void copyList(List<? extends T> source, List<? super T> destination) {
 *     for (T item : source) {
 *         destination.add(item);
 *     }
 * }
 * 实战应用：
 * 在实际开发中，我们常常需要根据需求选择合适的泛型通配符。例如，在处理只读取数据的列表时，可以使用 <? extends T>；而在处理需要写入数据的列表时，可以使用 <? super T>。
 * 结语
 * 理解和应用PECS原则是掌握Java泛型的重要一步。通过正确使用泛型通配符，我们可以编写出更为通用、灵活和类型安全的代码。希望这篇文章能够帮助你彻底搞懂Java泛型中的PECS原则，让你在编程的道路上少踩坑，多收获。
 */
